//
// Unihan Input Method
//
// Copyright © 2002-2007 Sun Wah Linux Ltd.
//
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
// USA
//
// $Id: canna.c 803 2007-07-31 15:22:52Z roger $
//

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <sys/types.h>
#include <canna/jrkanji.h>
#include <unihan_im.h>
#include <unihan_im_keycodes.h>
#include <iconv.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef _WCHAR_T
#define _WCHAR_T
#endif

static char canna_le_init_filename[] = ".canna";

#define CANNA_COMMIT_STRING_BUFSIZE (8192 * 2)
#define CANNA_CODESET_LOCALE "ja_JP.eucJP"
#define EUC_JP_SS2 0x8E
#define EUC_JP_SS3 0x8F
#define CANNA_NEXT_CHAR(p) 		\
  (((*p) == 0) ? (p) :			\
   (((*p) < 0x80) ? ((p) + 1) :		\
    (((*p) == EUC_JP_SS3) ? ((p) + 3) :	\
     ((p) + 2))))
#define CANNA_GUIDELINE_DELIMITER_P(p)	\
  (((*p) == ' ') || ((*p) == '\t') || 	\
   (((*p) == 0xA1) && ((p[1]) == 0xA1)))

/*
 * Unihan Methods
 */
static int mt_canna_init (UnihanIM *);
static int mt_canna_done (UnihanIM *);
static int mt_canna_create_session (UnihanIM *);
static int mt_canna_destroy_session (UnihanIM *);
static int mt_canna_handle_event (UnihanIM *, IMEvent *);

static char *eucJP_to_utf8(UnihanIM *uim, char *orig, int orig_len);

void		canna_status_draw_off(UnihanIM *);
static void	canna_change_mode(UnihanIM *uim, int id);
static int	canna_drop_privilege(const char *username);

typedef short UTFCHAR;

//UTFCHAR lename_string[] = {0x304B, 0x3093, 0x306A, 0x4C, 0x45, 0x0};
//UTFCHAR jahrn_string[] = {0x65E5, 0x672C, 0x8A9E, 0x0};
//UTFCHAR lookup_choice_title[] = {0x5019, 0x88DC, 0x9078, 0x629E, 0x0};
char lookup_choice_title[] = {0x50, 0x19, 0x88, 0xDC, 0x90, 0x78, 0x62, 0x9E, 0x0};



/*
  Desktop data:
 */

typedef struct
{
    int drop_priv;
    iconv_t jp_to_u8_cd;
    int status;
    int lookup;
    int preedit;
} CannaDataPerDesktop;

/*
  Session data:
*/

static int canna_context_id_counter = 1;

typedef struct
{
    int context_id;
    jrKanjiStatusWithValue ksv;
    int conversion_start;
} CannaDataPerSession;

CannaDataPerSession*
canna_session_data(UnihanIM *uim)
{
    return (CannaDataPerSession*)(uim->sessionUserData);
}

int
canna_session_context(UnihanIM *uim)
{
    return ((CannaDataPerSession*)(uim->sessionUserData))->context_id;
}

jrKanjiStatusWithValue*
canna_session_status(UnihanIM *uim)
{
    return &((CannaDataPerSession*)(uim->sessionUserData))->ksv;
}

static char *
eucJP_to_utf8(UnihanIM *uim, char *orig, int orig_len)
{
        CannaDataPerDesktop *dd = (CannaDataPerDesktop *)uim->desktopUserData;
        char *u8 = NULL, *u8_head, *euc, *euc_head;
        size_t ibytes, obytes;
#if DEBUG
	fprintf(stderr, "eucJP_to_utf8 ... ...\n");
#endif

        euc = euc_head = orig;
	if (! orig)
		return NULL;
        ibytes = orig_len;
	if (!ibytes)
		return NULL;
        obytes = orig_len * 3;
	if (!obytes)
		return NULL;
        uim->m->mem_alloc(uim, obytes, (void **)&u8);
        u8_head = u8;
        iconv(dd->jp_to_u8_cd, &euc, &ibytes, &u8, &obytes);
#if DEBUG
        fprintf(stderr, "Canna: eucJP_to_utf8(): ibytes = %d  obytes = %d u8 : %s\n",
			ibytes, obytes, u8_head);
#endif
	u8[obytes -1 ] = 0x0;
        
        return u8_head;
}

/*
  AUX Object:
*/

void init_objects()
{
}

/*
  Feedback operations:
*/

typedef enum {
    Canna_Feedback_Input,
    Canna_Feedback_Strong,
    Canna_Feedback_Normal,
}Canna_Feedback_Types;

char *
canna_string_to_utf8 (UnihanIM *uim,
		       int nseg, int *nb,
		       unsigned char **strs,
		       int *feedback_type)
{
    int clen = 0;
    char *str = NULL;
    int i;

    for (i = 0;i < nseg;i++) {
	clen += nb[i];
    }

    uim->m->mem_alloc (uim, clen*3, (void **)&str);
    memset (str, 0, clen);
    for (i=0; i<nseg; i++) {
	    char *u8 = eucJP_to_utf8(uim, strs[i], nb[i]);
	    if (u8)
		    strcat (str, u8);
    }

    return str;
}

/*
  Canna operations.
*/

static char*
canna_init_filename(char *user)
{
    char *buf;
    int ipsize;
    struct passwd *pw;

    if (!user)
	return NULL;
    setpwent();
    if ((pw = getpwnam(user)) == NULL) {
	endpwent();
	return NULL;
    }
    ipsize = strlen(pw->pw_dir);
    buf = (char*) malloc((ipsize + 2) * sizeof(char)
			 + sizeof(canna_le_init_filename));
    if (ipsize < 1) return NULL;
    strcpy(buf, pw->pw_dir);
    buf[ipsize] = '/';
    buf[ipsize + 1] = '\0';
    strcat(buf, canna_le_init_filename);

    endpwent();

    /* check whether the file is readable. */
    if (access(buf, R_OK) != 0) {
	free(buf);
	return NULL;
    }

    return buf;
}

int
canna_init(UnihanIM *uim, char *user)
{
    char **warning = NULL;
    char *init_filename;

    init_filename = canna_init_filename(user);
    if (init_filename) {
	jrKanjiControl(canna_session_context(uim), KC_SETINITFILENAME, init_filename);
	free(init_filename);
    }
    jrKanjiControl(canna_session_context(uim), KC_INITIALIZE, (char *) &warning);

    if (warning) {
	char          **p;

	for (p = warning; *p; p++)
	    fprintf(stderr, "CannaLE: %s\n", *p);

	return FALSE;
    }

    jrKanjiControl(canna_session_context(uim), KC_SETAPPNAME, (char *) "CannaLE");

    /* set user info */
    if (user) {
	jrUserInfoStruct info;

	memset(&info, 0, sizeof (info));
	info.uname = user;
	jrKanjiControl(canna_session_context(uim), KC_SETUSERINFO, (char *)&info);
    }
    

    return TRUE;
}

void
canna_status_draw(UnihanIM *uim)
{
    CannaDataPerSession *pcls = canna_session_data(uim);
    unsigned char *str;
    char *u8;
    int len;

    jrKanjiStatusWithValue *pksv;
    pksv = canna_session_status(uim);

    len = jrKanjiControl(canna_session_context(uim), KC_QUERYMAXMODESTR, 0);
    str = (unsigned char *)uim->m->mem_alloc(uim, sizeof(unsigned char)
		    * (len+1), (void**)&str);
    jrKanjiControl(canna_session_context(uim), KC_QUERYMODE, str);
    u8 = eucJP_to_utf8 (uim, str, len+1);
    uim->m->status_draw (uim, u8, NULL);
}

void
canna_preedit_draw(UnihanIM *uim)
{
    char *p;

    CannaDataPerDesktop *dd = (CannaDataPerDesktop *)uim->desktopUserData;
    CannaDataPerSession *pcls = canna_session_data(uim);
    jrKanjiStatus *pks = canna_session_status(uim)->ks;

    /* When KanjiStatus is uninited, return immediately. */
    if (!pks->echoStr) {
	    uim->m->preedit_erase(uim);
	    return;
    }

    //if (! dd->preedit) {
	    //uim->m->preedit_start (uim);
//	    dd->preedit = 1;
 //   }

    {
	/* Create IMText with feedbacks.  */
	int nb[3], fts[3];
	unsigned char *strs[3];
	nb[0] = pks->revPos;
	nb[1] = pks->revLen;
	nb[2] = pks->length - nb[0] - nb[1];
	fts[0] = Canna_Feedback_Input;
	fts[1] = Canna_Feedback_Strong;
	fts[2] = Canna_Feedback_Input;
	strs[0] = pks->echoStr;
	strs[1] = strs[0] + pks->revPos;
	strs[2] = strs[1] + pks->revLen;
	p = canna_string_to_utf8 (uim, 3, nb, strs, fts);
    }

    fprintf(stderr, "Canna : preedit draw string %s ()\n", p);

    if (!p)
	    uim->m->preedit_erase(uim);
    else
	    uim->m->preedit_draw (uim, p, NULL);
}

void
canna_status_draw_off(UnihanIM *uim)
{
    unsigned char *str = "";

    uim->m->status_draw (uim, str, NULL);
}

void
canna_preedit_done(UnihanIM *uim)
{
	uim->m->preedit_erase (uim);
}


/*
  Caution!!!
  This part assumes the structure of guidline given by canna UI
  library.  It parses guideline string without any protocol on it.
  Therefore, it may not work with the future version of canna
  UI library!
*/
int
canna_parse_guideline(UnihanIM *uim, int *pnum,
		      unsigned char ***psegs,
		      int **pnb,
		      int *pcurrent)
{
#if 1
    char linestr[1024];
#endif
    jrKanjiStatus *pks = canna_session_status(uim)->ks;
    unsigned char *str = pks->gline.line;
    unsigned char *p, *st;
    int i, idx, tot, delimiterp;

    tot = 0;
    for (p = str, st = NULL;*p;p = CANNA_NEXT_CHAR(p)) {
	delimiterp = CANNA_GUIDELINE_DELIMITER_P(p);
	if (st && delimiterp) {
	    tot++;
	    st = NULL;
	}else if (!st && !delimiterp) {
	    st = p;
	}
    }
    *pnum = tot;
    *pcurrent = 0;
    *psegs = (unsigned char**) malloc(sizeof(unsigned char*) * tot * 2);
    *pnb = (int*) malloc(sizeof(int) * tot * 2);
    for (p = str, i = 0, idx = 0, st = NULL;
	 (idx < tot);p = CANNA_NEXT_CHAR(p)) {
	delimiterp = CANNA_GUIDELINE_DELIMITER_P(p);
	if (st && delimiterp) {
	    /* the size of the value */
	    (*pnb)[i] = (p - st);
	    i++;
	    idx++;
	    st = NULL;
	}else if (!st && !delimiterp) {
	    /* label */
	    (*psegs)[i] = st = p;
	    (*pnb)[i] = CANNA_NEXT_CHAR(p) - p;
	    i++;
	    if (pks->gline.revPos == (p - str))
		*pcurrent = idx;
	    /* value */
	    (*psegs)[i] = CANNA_NEXT_CHAR(p);
	}
    }
#if 1
    for (i = 0;i < (tot * 2);i++) {
	memcpy(linestr, (*psegs)[i], (*pnb)[i]);
	linestr[(*pnb)[i]] = '\0';
	fprintf(stderr, "Seg(%d):%s\n", i, linestr);
    }
#endif
    return TRUE;
}

void
canna_start_lookup_choice(UnihanIM *uim, int num)
{
	CannaDataPerDesktop *dd = (CannaDataPerDesktop *)uim->desktopUserData;
    CannaDataPerSession *pcls = canna_session_data(uim);
    UnihanLookupStart start;
    if (! dd->lookup) {
	    start.choicePerWindow = num;
	    start.numColumns = 9;
	    start.numRows = 1;
	    start.direction = UNIHAN_LOOKUP_DIRECTION_RIGHT;
	    uim->m->lookup_start (uim, &start);
	    dd->lookup = 1;
    }
}

void
canna_show_lookup_choice(UnihanIM *uim)
{
	int num;
	CannaDataPerSession *pcls = canna_session_data(uim);
	jrKanjiStatus *pks = canna_session_status(uim)->ks;
	UnihanLookupDraw draw;
	int i, cid;
	unsigned char **ps;
	int *segs;
	int max_len = 0;
#if DEBUG
	fprintf(stderr, "Canna : show lookup choice ()\n");
#endif

	/* When KanjiStatus is uninited, return immediately. */
	if (!pks->gline.line) return;

	
	if (!canna_parse_guideline(uim, &num, &ps, &segs, &cid))
	   return;

	draw.mode = UNIHAN_LOOKUP_MODE_NORMAL;
	draw.numChoices = num;
	draw.selected = cid;
	draw.currentChoice = cid;
	draw.totalChoices = num;
	draw.title = lookup_choice_title;
	uim->m->mem_alloc (uim, sizeof(char*) * draw.numChoices, (void**)&draw.label);
	uim->m->mem_alloc (uim, sizeof(char*) * draw.numChoices, (void**)&draw.value);

	for (cid=0, i=0; i<draw.numChoices; ++i) {
		draw.label[i] =
			canna_string_to_utf8(uim, 1, &segs[cid], &ps[cid], NULL);
		cid++;
		draw.value[i] =
			canna_string_to_utf8(uim, 1, &segs[cid], &ps[cid], NULL);
		cid++;
	}
	free (ps);
	free (segs);
	//draw->max_len = max_len;

	canna_start_lookup_choice(uim, 9);

	uim->m->lookup_draw (uim, &draw);
}

void
canna_lookup_choice_done(UnihanIM *uim)
{
	CannaDataPerDesktop *dd = (CannaDataPerDesktop *)uim->desktopUserData;
	if (dd->lookup) {
		uim->m->lookup_done (uim);
		dd->lookup = 0;
	}
}

int
canna_translate_keyevent(IMKeyEvent *k)
{
    //IMKeyEvent *k = (IMKeyEvent *) kev->keylist;
    printf("iml_session_t() keycode=%x,keychar=%x, state=%x\n",
	   k->keyCode, k->keyChar, k->modifier);

    switch(k->keyCode) {
    case IM_VK_UP:
	if (k->modifier & IM_CTRL_MASK) {
	    return CANNA_KEY_Cntrl_Up;
	} else if (k->modifier & IM_SHIFT_MASK) {
	    return CANNA_KEY_Shift_Up;
	}
	return  CANNA_KEY_Up;

    case IM_VK_DOWN:
	if (k->modifier & IM_CTRL_MASK) {
	    return CANNA_KEY_Cntrl_Down;
	} else if (k->modifier & IM_SHIFT_MASK) {
	    return CANNA_KEY_Shift_Down;
	}
	return  CANNA_KEY_Down;

    case IM_VK_LEFT:
	if (k->modifier & IM_CTRL_MASK) {
	    return CANNA_KEY_Cntrl_Left;
	} else if (k->modifier & IM_SHIFT_MASK) {
	    return CANNA_KEY_Shift_Left;
	}
	return  CANNA_KEY_Left;

    case IM_VK_RIGHT:
	if (k->modifier & IM_CTRL_MASK) {
	    return CANNA_KEY_Cntrl_Right;
	} else if (k->modifier & IM_SHIFT_MASK) {
	    return CANNA_KEY_Shift_Right;
	}
	return  CANNA_KEY_Right;

    case IM_VK_INSERT:
	return CANNA_KEY_Insert;

    case IM_VK_PAGE_UP:
	return CANNA_KEY_Rolldown;

    case IM_VK_PAGE_DOWN:
	return CANNA_KEY_Rollup;

    case IM_VK_HOME:
	return CANNA_KEY_Home;

    case IM_VK_HELP:
	return CANNA_KEY_Help;

    case IM_VK_F1:
	/* CANNA_KEY_PF1 */
	return CANNA_KEY_F1;

    case IM_VK_CONVERT: /* XFER */
	if (k->modifier & IM_CTRL_MASK) {
	    return CANNA_KEY_Cntrl_Xfer;
	} else if (k->modifier & IM_SHIFT_MASK) {
	    return CANNA_KEY_Shift_Xfer;
	}
	return CANNA_KEY_Xfer;

    case IM_VK_NONCONVERT: /* NFER */
	if (k->modifier & IM_CTRL_MASK) {
	    return CANNA_KEY_Cntrl_Nfer;
	} else if (k->modifier & IM_SHIFT_MASK) {
	    return CANNA_KEY_Shift_Nfer;
	}
	return CANNA_KEY_Nfer;

    case IM_VK_ENTER:
	return 0x0D;

    case IM_VK_BACK_SPACE:
	return 0x08;

    case IM_VK_DELETE:
	return 0x04;

    default:
	if (k->modifier & IM_CTRL_MASK) {
	    /* no process any Ctrl characters here */
	    break;
	}
	if ((k->keyChar > 0)
	    && (k->keyChar < 0xFFFF)) {
	    /* Should we translate it to EUC? */
	    return k->keyChar;
	}
    }

    fprintf(stderr, "translation failed:keycode=%x,keychar=%x, state=%x\n",
	    k->keyCode, k->keyChar, k->modifier);
    return 0;
}

int
canna_process_keyevent(UnihanIM *uim, IMKeyEvent *kev)
{
    int ch;
    ch = canna_translate_keyevent(kev);

    if (ch) {
	int size, n;
	char buf[CANNA_COMMIT_STRING_BUFSIZE + 1];

	jrKanjiStatus *pks;
	pks = canna_session_status(uim)->ks;
	size = CANNA_COMMIT_STRING_BUFSIZE;
	n = jrKanjiString(canna_session_context(uim),
			  ch, buf, size, pks);
	fprintf(stderr, "jrKanjiString get n : %d buf : %s ()\n", n, buf);
	buf[n] = '\0';

	/* if there is no preedit characters and no related characters,
	 * shouldn't process it.
	 */
	if (n == 1 && (pks->info & KanjiThroughInfo) && pks->length == 0) {
		pks->info &= ~KanjiThroughInfo;
		goto no_process;
	}
	if (n > 0) {
		char *u8;
		pks->info &= ~KanjiThroughInfo;
		u8 = eucJP_to_utf8(uim, buf, n); 
		uim->m->commit_string (uim, u8);
	}
	if (pks->length >= 0)
	    canna_preedit_draw(uim);
	//if (pks->info & KanjiModeInfo)
	 //   canna_status_draw(uim);
	if (pks->info & KanjiGLineInfo)	{
	    if (pks->gline.length > 0)
		canna_show_lookup_choice(uim);
	    else
		canna_lookup_choice_done(uim);
	}
	return TRUE;
    }else{
no_process:;
	/* I don't process this keyevent.  Return it. */
	//iml_inst *lp;
	//lp = s->If->m->iml_make_keypress_inst(s, ((IMKeyEventStruct *)
	//					  kev->keylist));
	//s->If->m->iml_execute(s, &lp);
    }
    return FALSE;
}

int
unihan_im_module_init (void)
{
	int st;
#if DEBUG
	fprintf(stderr, "Canna : module init ... \n");
#endif
	return TRUE;
}

void
unihan_im_module_done (void)
{
	return;
}

int
unihan_im_module_get_info (UnihanIMModuleArgList *args)
{
#if DEBUG
	fprintf(stderr, "Canna : im module get info ... \n");
#endif
	IMInfo *ii;

	if (! args) return FALSE;

	while (args->id) {
		switch (args->id) {
			case UNIHAN_IM_MODULE_ID:
				fprintf(stderr, "IM MODULE ID\n");
				args->value = (void*)strdup ("canna");
				break;
			case UNIHAN_IM_MODULE_NAME:
				fprintf(stderr, "IM MODULE NAME\n");
				args->value = (void*)strdup ("Canna");
				break;
			case UNIHAN_IM_MODULE_VERSION:
				fprintf(stderr, "IM MODULE VERSION\n");
				args->value = (void*)strdup ("1.0");
				break;
			case UNIHAN_IM_MODULE_IM_LIST:
				fprintf(stderr, "UNIHAN IM MODULE IM LIST\n");
				ii = (IMInfo *)malloc (sizeof (IMInfo));
				memset (ii, 0, sizeof (IMInfo));

				ii->id = strdup ("canna");
				ii->name = strdup ("Canna");
				ii->version = strdup ("1.2");

				ii->methodTable = (IMMethods *)malloc(sizeof (IMMethods));
				memset (ii->methodTable, 0, sizeof(IMMethods));

				ii->methodTable->init = mt_canna_init;
				ii->methodTable->done = mt_canna_done;
				ii->methodTable->create_session = mt_canna_create_session;
				ii->methodTable->destroy_session = mt_canna_destroy_session;
				ii->methodTable->handle_event = mt_canna_handle_event;

				args->value = ii;
				break;
			case UNIHAN_IM_API_VERSION:
				fprintf(stderr, "IM API VERSION\n");
				args->value = (void*)strdup ("2.0");
				break;
		}
		++args;
	}

	return TRUE;
}

//static char *ops[10][50];
//static char ***ps = ops;

static int
mt_canna_init (UnihanIM *uim)
{
#if DEBUG
	fprintf(stderr, "Canna : canna init ... \n");
#endif
	CannaDataPerDesktop *dd = NULL;
	//jrKanjiStatusWithValue *pksv;

	dd = (CannaDataPerDesktop *)malloc(sizeof(CannaDataPerDesktop));
	memset (dd, 0, sizeof(CannaDataPerDesktop));
	dd->drop_priv = canna_drop_privilege(uim->user_name);
	dd->jp_to_u8_cd = iconv_open ("UTF-8", "eucJP");
	uim->desktopUserData = (void *) dd;

	canna_context_id_counter = 1;

	return TRUE;
}

static int
mt_canna_done (UnihanIM *uim)
{
	CannaDataPerDesktop *dd = (CannaDataPerDesktop *)uim->desktopUserData;
	iconv_close (dd->jp_to_u8_cd);

	free (dd);

	return TRUE;
}

static int
mt_canna_create_session (UnihanIM *uim)
{
    CannaDataPerSession *pcls = (CannaDataPerSession*) malloc(sizeof(CannaDataPerSession));
    jrKanjiStatus *pks = (jrKanjiStatus*) malloc(sizeof(jrKanjiStatus));
    //iml_desktop_t *desktop = s->desktop;
    CannaDataPerDesktop *dd = (CannaDataPerDesktop *) uim->desktopUserData;
    unsigned char *buf;
#if DEBUG
    fprintf(stderr, "Canna : canna create session \n");
#endif

    buf = (unsigned char *) malloc(CANNA_COMMIT_STRING_BUFSIZE);
    if ((!pcls) || (!pks) || (!buf)) return FALSE;
    pcls->ksv.ks = pks;
    pcls->ksv.buffer = buf;
    buf[0] = '\0';
    pcls->ksv.bytes_buffer = CANNA_COMMIT_STRING_BUFSIZE;
    pcls->conversion_start = FALSE;
    pcls->context_id = canna_context_id_counter++;
    /* Init jrKanjiStatus variable with 0. */
    memset(pks, 0, sizeof(jrKanjiStatus));

    uim->sessionUserData = (void*) pcls;

    /* initialize here because of avoid wrong initialization for multiple users */
    if (canna_init(uim, dd->drop_priv ? uim->user_name : NULL)) {
	    fprintf(stderr, "Canna : canna init successful ...\n");
	//TODO
	//to add correct code for testing connection.
	//if (!jrKanjiControl(canna_session_context(uim), KC_QUERYCONNECTION, (char *) 0)) {
	//    fprintf(stderr, "CannaLE: Unable to connect with canna server.\n");
	//    return FALSE;
	//}
    }
    if (canna_get_current_mode (uim) != CANNA_MODE_HenkanMode) {
		fprintf(stderr, "Canna : set canna mode CANNA_MODE_HenkanMode\n");
		canna_change_mode (uim, CANNA_MODE_HenkanMode);
	}
    fprintf(stderr, "Canna : create session OK !!!\n");
    return TRUE;
}

static int
mt_canna_destroy_session (UnihanIM *uim)
{
    CannaDataPerSession *pcls;

#if DEBUG
    fprintf(stderr, "Canna : canna destroy session \n");
#endif
    canna_change_mode (uim, CANNA_MODE_AlphaMode);
	//canna_status_draw (uim);
	//canna_status_draw_off (uim);
	canna_lookup_choice_done (uim);
	canna_preedit_done (uim);

    pcls = canna_session_data(uim);
    jrKanjiControl(canna_session_context(uim), KC_FINALIZE, (char *) 0);
    if (pcls->ksv.buffer) free(pcls->ksv.buffer);
    if (pcls->ksv.ks) free(pcls->ksv.ks);
    free(pcls);
    return TRUE;
}

static int
mt_canna_handle_event (UnihanIM *uim, IMEvent *ev)
{
	int ret = FALSE;

#if DEBUG
	fprintf(stderr, "Canna : handle event ... \n");
#endif

	switch (ev->type) {
		case IM_KEY:
			return canna_process_keyevent (uim, (IMKeyEvent *)ev);
			break;
		case IM_SWITCH:
			break;
		case IM_FOCUS:
			//if (((IMFocusEvent *)ev)->focused)
				//canna_status_draw (uim);
			//else
			//	canna_status_draw_off (uim);
			break;
		default:
			break;
	}
	return TRUE;
}

static void
canna_change_mode(UnihanIM *uim, int id)
{
    CannaDataPerSession *pcls = canna_session_data(uim);
    jrKanjiStatusWithValue *pksv;

    pksv = canna_session_status(uim);
    pksv->val = id;
    jrKanjiControl(canna_session_context(uim),
		   KC_CHANGEMODE, (char*) pksv);
}

int
canna_get_current_mode(UnihanIM *uim)
{
    CannaDataPerSession *pcls = canna_session_data(uim);
    char mode[4];

    jrKanjiControl(canna_session_context(uim), KC_SETMODEINFOSTYLE, (char *) 1);
    jrKanjiControl(canna_session_context(uim), KC_QUERYMODE, (char *)mode);
    jrKanjiControl(canna_session_context(uim), KC_SETMODEINFOSTYLE, (char *) 0);

    return mode[0] - '@';
}

static int
canna_drop_privilege(const char *username)
{
    struct passwd *pw;
    uid_t uid;
    gid_t gid;

    if (!username)
	goto error;
    if ((pw = getpwnam(username)) == NULL) {
	goto error;
    } else {
	uid = pw->pw_uid;
	gid = pw->pw_gid;
	if (uid < 500)
	    goto error;
    }
    if (setregid(gid, gid) < 0) {
	return FALSE;
    }
    if (setreuid (uid, uid) < 0) {
	return FALSE;
    }
    return TRUE;

error:
    if ((pw = getpwnam("nobody")) != NULL) {
	gid = pw->pw_gid;
	uid = pw->pw_uid;
	setregid(gid, gid);
	setreuid(uid, uid);
    } else {
	assert(0);
    }
    return FALSE;
}

/* Local Variables: */
/* c-file-style: "bsd" */
/* End: */

// vi:ts=4:nowrap:cin:expandtab
